package com.net.noah.core.service.impl;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.net.noah.core.domain.SerialNumber;
import com.net.noah.core.service.SerialNumberService;
import com.net.common.exception.SystemException;
import com.net.noah.core.service.SerialNumberGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
  on 02/05/2018.
 * 事务必须配置在最外层
 */
@Service
public class SerialNumberServiceImpl implements SerialNumberService {
    private static final String COMMON_SN_NAME = "common";

    private static final int SN_MIN_LENGTH = 8; //to-attention:有可能会溢出(8位刚好可以,超过8位会溢出)

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");

    @Autowired
    private SerialNumberGenerator serialNumberGenerator;

    private Map<String/*tableName*/, SerialNumber> serialNumberMap = new ConcurrentHashMap<>();
    private Map<String/*tableName*/, Long/*current*/> currentMap = new ConcurrentHashMap<>();

    @Override
    public long getSerialNumber() {
        long sn = getSerialNumber(COMMON_SN_NAME);
        String strSn = Strings.padStart("" + sn, SN_MIN_LENGTH, '0');
        return Long.valueOf(LocalDate.now().format(DATE_TIME_FORMATTER) + strSn);
    }

    @Override
    public long getLongSerialNumber(String tableName) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(tableName));
        long sn = getSerialNumber(tableName);
        String strSn = Strings.padStart("" + sn, SN_MIN_LENGTH, '0');
        return Long.valueOf(LocalDate.now().format(DATE_TIME_FORMATTER) + strSn);
    }

    @Override
    public long getSerialNumberByTableName(String tableName) {

        return getSerialNumber(tableName);
    }

    private synchronized long getSerialNumber(String name) {
        SerialNumber serialNumber = serialNumberMap.get(name);
        if (serialNumber == null) {
            serialNumber = fetchAndUpdateSerialNumber(name);
        }

        if (serialNumber == null) {
            throw new SystemException("获取sn服务失败|name={}", name);
        }
        Long current = currentMap.get(name);
        if (current >= serialNumber.getNextVal()) {
            serialNumber = fetchAndUpdateSerialNumber(name);
            current = currentMap.get(name);
        }
        if (current >= serialNumber.getNextVal()) {
            throw new SystemException("获取sn服务失败,current过大|name={}", name);
        }
        current += 1;
        currentMap.put(name, current);
        return current - 1;
    }

    private synchronized SerialNumber fetchAndUpdateSerialNumber(String name) {
        SerialNumber serialNumber = serialNumberGenerator.getSerialNumber(name);
        currentMap.put(name, serialNumber.getNextVal());
        serialNumber.setNextVal(serialNumber.getNextVal() + serialNumber.getStep());
        serialNumberMap.put(name, serialNumber);

        return serialNumber;
    }
}
